/*****************************************************************************
 * Copyright [2019] 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *****************************************************************************/

#include <Data/datamanager.h>
#include <Trade/portfoliomanager.h>
#include <Common/datastruct.h>
#include <yaml-cpp/yaml.h>
#include <fmt/format.h>
#include <vector>
#include <fstream>
#include <boost/algorithm/algorithm.hpp>


namespace StarQuant {
DataManager* DataManager::pinstance_ = nullptr;
mutex DataManager::instancelock_;

DataManager::DataManager() : count_(0) {
    loadSecurityFile();
}

DataManager::~DataManager() {}

DataManager& DataManager::instance() {
    if (pinstance_ == nullptr) {
        lock_guard<mutex> g(instancelock_);
        if (pinstance_ == nullptr) {
            pinstance_ = new DataManager();
        }
    }
    return *pinstance_;
}



void DataManager::updateOrderBook(const Fill& fill) {
// assume only price change
    if (orderBook_.find(fill.fullSymbol_) != orderBook_.end()) {
        orderBook_[fill.fullSymbol_].price_ = fill.tradePrice_;
        orderBook_[fill.fullSymbol_].size_ = fill.tradeSize_;
    } else {
        Tick newk;
        newk.depth_ = 0;
        newk.price_ = fill.tradePrice_;
        newk.size_ = fill.tradeSize_;
        orderBook_[fill.fullSymbol_] = newk;
    }
}

void DataManager::loadSecurityFile() {
    try {
        string contractpath = boost::filesystem::current_path().string() + "/etc/ctpcontract.yaml";
        YAML::Node contractinfo = YAML::LoadFile(contractpath);
        for (YAML::const_iterator symsec = contractinfo.begin(); symsec != contractinfo.end(); symsec++) {
            auto sym = symsec->first.as<std::string>();
            auto securities = symsec->second;
            Security sec;
            sec.symbol_ = securities["symbol"].as<std::string>();
            sec.exchange_ = securities["exchange"].as<std::string>();
            sec.securityType_ = securities["product"].as<char>();
            sec.multiplier_ = securities["size"].as<int32_t>();
            sec.localName_ = securities["name"].as<std::string>();
            sec.ticksize_ = securities["pricetick"].as<double>();
            sec.postype_ = securities["positiontype"].as<char>();
            sec.longMarginRatio_ = securities["long_margin_ratio"].as<double>();
            sec.shortMarginRatio_ = securities["short_margin_ratio"].as<double>();
            sec.underlyingSymbol_ = securities["option_underlying"].as<std::string>();
            sec.optionType_ = securities["option_type"].as<char>();
            sec.strikePrice_ = securities["option_strike"].as<double>();
            sec.expiryDate_ = securities["option_expiry"].as<std::string>();
            sec.fullSymbol_ =  securities["full_symbol"].as<std::string>();
            securityDetails_[sym] = sec;
            ctp2Full_[sym] = sec.fullSymbol_;
            full2Ctp_[sec.fullSymbol_] = sym;
        }
        // back up
        std::ofstream fout("etc/ctpcontract.yaml.bak");
        fout<< contractinfo;
    }
    catch(exception &e) {
        fmt::print("Read contract exception:{}.", e.what());
    }
    catch(...) {
        fmt::print("Read contract error!");
    }
}

void DataManager::saveSecurityToFile() {
    try {
        YAML::Node securities;
        for (auto iterator = securityDetails_.begin(); iterator != securityDetails_.end(); ++iterator) {
            auto sym = iterator->first;
            auto sec = iterator->second;
            securities[sym]["symbol"] = sec.symbol_;
            securities[sym]["exchange"] = sec.exchange_;
            securities[sym]["product"] = sec.securityType_;
            securities[sym]["size"] = sec.multiplier_;
            securities[sym]["name"] = sec.localName_;
            securities[sym]["pricetick"] = sec.ticksize_;
            securities[sym]["positiontype"] = sec.postype_;
            securities[sym]["long_margin_ratio"] = sec.longMarginRatio_;
            securities[sym]["short_margin_ratio"] = sec.shortMarginRatio_;
            securities[sym]["option_underlying"] = sec.underlyingSymbol_;
            securities[sym]["option_type"] = sec.optionType_;
            securities[sym]["option_strike"] = sec.strikePrice_;
            securities[sym]["option_expiry"] = sec.expiryDate_;
            string fullsym;
            string type;
            string product;
            string contracno;
            int32_t i;
            if (sec.securityType_ == '1' || sec.securityType_ == '2') {
                for (i = 0; i < sym.size(); i++) {
                    if (isdigit(sym[i]))
                        break;
                }
                product = sym.substr(0, i);
                contracno = sym.substr(i);
                type = (sec.securityType_ == '1'? "F":"O");
                fullsym = sec.exchange_ + " " + type + " " + boost::to_upper_copy(product) + " " + contracno;
            } else if (sec.securityType_ == '3') {
                int32_t pos = sym.find(" ");
                string combo = sym.substr(pos+1);
                int32_t sep = combo.find("&");
                string sym1 = combo.substr(0, sep);
                string sym2 = combo.substr(sep+1);
                for (i = 0; i < sym1.size(); i++) {
                    if (isdigit(sym1[i]))
                        break;
                }
                product = sym1.substr(0, i) + "&";
                contracno = sym1.substr(i) + "&";
                for (i = 0; i < sym2.size(); i++) {
                    if (isdigit(sym2[i]))
                        break;
                }
                product += sym2.substr(0, i);
                contracno += sym2.substr(i);
                fullsym = sec.exchange_ + " " + "S" + " " + boost::to_upper_copy(product) + " " + contracno;						
            } else {
                fullsym = sec.exchange_ + " " + sec.securityType_ + " " + sym;
            }
            securities[sym]["full_symbol"] = fullsym;
            ctp2Full_[sym] = fullsym;
            full2Ctp_[fullsym] = sym;
            securityDetails_[sym].fullSymbol_ = fullsym;
        }
        std::ofstream fout("etc/ctpcontract.yaml");
        fout<< securities;
    }
    catch(exception &e) {
        fmt::print("Write Contract exception:{}.", e.what());
    }
    catch(...) {
        fmt::print("Write Contract error!");
    }
}



void DataManager::reset() {
    orderBook_.clear();
    //_5s.clear();
    //_15s.clear();
    // _60s.clear();
    //_1d.clear();
    securityDetails_.clear();
    count_ = 0;
}






void DataManager::rebuild() {
    // for (auto& s : CConfig::instance().securities) {
    // 	Tick_L5 k;
    // 	k.fullsymbol_ = s;
    // 	_latestmarkets[s] = k;

    // 	//_5s[s].fullsymbol = s; _5s[s].interval_ = 5;
    // 	//_15s[s].fullsymbol = s; _15s[s].interval_ = 15;
    // 	// _60s[s].fullsymbol = s; _60s[s].interval_ = 60;
    // 	//_1d[s].fullsymbol = s; _1d[s].interval_ = 24 * 60 * 60;
    // }
}

// void DataManager::SetTickValue(Tick& k) {
    // PRINT_TO_FILE("INFO:[%s,%d][%s]%s, %.2f\n", __FILE__, __LINE__, __FUNCTION__, k.fullsymbol_.c_str(), k.price_);		// for debug
    // //cout<<"settickvalue ktype: "<<k.msgtype_<<" price"<<k.price_<<endl;
    // if (k.msgtype_ == MSG_TYPE::MSG_TYPE_Bid) {
    // 	_latestmarkets[k.fullsymbol_].bidprice_L1_ = k.price_;
    // 	_latestmarkets[k.fullsymbol_].bidsize_L1_ = k.size_;
    // }
    // else if (k.msgtype_ == MSG_TYPE::MSG_TYPE_Ask ){
    // 	_latestmarkets[k.fullsymbol_].askprice_L1_ = k.price_;
    // 	_latestmarkets[k.fullsymbol_].asksize_L1_ = k.size_;
    // }
    // else if (k.msgtype_ == MSG_TYPE::MSG_TYPE_Trade) {
    // 	_latestmarkets[k.fullsymbol_].price_ = k.price_;
    // 	_latestmarkets[k.fullsymbol_].size_ = k.size_;

    // 	//_5s[k.fullsymbol_].newTick(k);				// if it doesn't exist, operator[] creates a new element with default constructor
    // 	//_15s[k.fullsymbol_].newTick(k);
    // 	_60s[k.fullsymbol_].newTick(k);
    // 	//PortfolioManager::instance().positions_[sym].
    // }
    // else if (k.msgtype_ == MSG_TYPE::MSG_TYPE_TICK_L1 || k.msgtype_ == MSG_TYPE::MSG_TYPE_TICK_L5 || k.msgtype_ == MSG_TYPE::MSG_TYPE_TICK_L20 ) {
    // 	//_latestmarkets[k.fullsymbol_] = dynamic_cast<Tick_L5&>(k);		// default assigement shallow copy
    // 	_latestmarkets[k.fullsymbol_].price_ = k.price_;
    // 	_latestmarkets[k.fullsymbol_].size_ = k.size_;
    // 	//cout<<"settickvalue price"<<_latestmarkets[k.fullsymbol_].price_<<endl;
    // 	_60s[k.fullsymbol_].newTick(k);
    // }
// }


}  // namespace StarQuant

